package app

import (
	"fmt"
	"go_learn/actor"
	"go_learn/mlog"
	"sync"
)

type DefaultApp struct {
	actorDict       map[uint]actor.Actor
	actorName       map[string]uint //该actor的数目
	uniqueActorDict map[string]uint //name->addr
	actorIndex      uint
	locker          *sync.RWMutex
	closeSig        chan bool
}

var (
	App = NewApp()
)

func (slf *DefaultApp) Send(target interface{}, msg *actor.RpcMsg) (err error) {
	slf.locker.RLock()
	defer slf.locker.RUnlock()
	var addr uint
	switch target.(type) {
	case uint:
		addr = target.(uint)
	case string:
		addr = slf.uniqueActorDict[target.(string)]
	default:
		mlog.Errorf("can't recive addr type")
		err = fmt.Errorf("can't recive addr type")
		return err
	}
	service, ok := slf.actorDict[addr]
	if !ok {
		err = fmt.Errorf("can't find target actor %d", addr)
		return err
	}
	rchan := service.GetRpcChan()
	rchan <- msg
	return
}

func (slf *DefaultApp) uniqueActor(actor actor.Actor, s actor.Service) (addr uint, err error) {
	slf.locker.Lock()
	defer slf.locker.Unlock()
	if addr, ok := slf.uniqueActorDict[s.Name()]; ok {
		return addr, nil
	}
	if num, ok := slf.actorName[s.Name()]; ok && num > 0 {
		return 0, fmt.Errorf("There Is A Repeated Actor")
	}
	addr = slf.actorIndex
	slf.actorIndex++
	slf.actorDict[addr] = actor
	slf.actorName[s.Name()]++
	slf.uniqueActorDict[s.Name()] = addr
	return addr, err
}

func (slf *DefaultApp) UniqueActor(actor actor.Actor, s actor.Service) (addr uint, err error) {
	addr, err = slf.uniqueActor(actor, s)
	actor.OnInit(slf, s, addr)
	actor.Run(slf.closeSig)
	return addr, err
}

func (slf *DefaultApp) newActor(actor actor.Actor, s actor.Service) (addr uint, err error) {
	slf.locker.Lock()
	defer slf.locker.Unlock()
	if _, ok := slf.uniqueActorDict[s.Name()]; ok {
		return 0, fmt.Errorf("There Is A Unique Actor")
	}
	addr = slf.actorIndex
	slf.actorIndex++
	slf.actorDict[addr] = actor
	slf.actorName[s.Name()]++
	return addr, err
}

func (slf *DefaultApp) NewActor(actor actor.Actor, s actor.Service) (addr uint, err error) {
	addr, err = slf.newActor(actor, s)
	actor.OnInit(slf, s, addr)
	actor.Run(slf.closeSig)
	return addr, err
}

func (slf *DefaultApp) removeActor(actor actor.Actor) (err error) {
	slf.locker.Lock()
	defer slf.locker.Unlock()
	addr := actor.GetAddr()
	if _, ok := slf.actorDict[addr]; !ok {
		return
	}
	name := actor.GetService().Name()
	delete(slf.actorDict, addr)
	slf.actorName[name]--
	if val, ok := slf.uniqueActorDict[name]; ok && val == addr {
		delete(slf.uniqueActorDict, name)
	}
	return
}

func (slf *DefaultApp) ActorExit(actor actor.Actor) (err error) {
	slf.removeActor(actor)
	actor.OnDestroy()
	return
}

func NewApp() *DefaultApp {
	app := new(DefaultApp)
	app.actorDict = make(map[uint]actor.Actor)
	app.actorName = make(map[string]uint)
	app.uniqueActorDict = make(map[string]uint)
	app.actorIndex = 1
	app.locker = new(sync.RWMutex)
	app.closeSig = make(chan bool)
	return app
}
